﻿/*
	Copyright 2010 Intuitive Solutions

	Licensed under the Apache License, Version 2.0 (the "License");
	you may not use this file except in compliance with the License.
	You may obtain a copy of the License at

	   http://www.apache.org/licenses/LICENSE-2.0

	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
*/

using System;
using Metamorph.Core.Bpm.Events;
using System.Collections.Generic;
using Metamorph.Core.Bpm.Connectors;
using Metamorph.Core.Bpm.Activities;

namespace Metamorph.Core.Bpm
{
	public class BusinessProcess : IBusinessProcess
	{
		protected ISubprocess				pProcess;
		protected bool						bInterrupt;
		protected IShape					sCurrentShape;
		protected Stack<ExecutionContext>	sExecutionStack;
		protected object					oCurrentMessage;
		protected Exception					eCurrentExeption;
		protected Stack<ISubprocess>		sSubprocessStack;
		protected IProcessContext			pcProcessContext;
		protected string					sId, sHierarchyId;

		public BusinessProcess(ISubprocess pProcess, string sId)
		{
			this.sId			= sId;
			bInterrupt			= false;
			this.pProcess		= pProcess;
			sHierarchyId		= string.Empty;
			sCurrentShape		= pProcess.InitialShape;
			sSubprocessStack	= new Stack<ISubprocess>();
			sExecutionStack		= new Stack<ExecutionContext>();
		}

		public void PushProcess(ISubprocess sSubprocess)
		{
			sHierarchyId += "." + sSubprocess.Name;

			sSubprocessStack.Push(sSubprocess);
		}

		public void PopProcess()
		{
			sHierarchyId = sHierarchyId.Substring(0, sHierarchyId.LastIndexOf("." + sSubprocessStack.Peek().Name));

			sSubprocessStack.Pop();
		}

		#region IBusinessProcess Members

		public void Init()
		{
			sCurrentShape = pProcess;

			sCurrentShape.Init(this);
		}

		public void Next()
		{
			try
			{
				while (sExecutionStack.Count > 0 &&
						!bInterrupt)
				{
					if (sExecutionStack.Peek().CurrentShape == null)
					{
						sExecutionStack.Peek().Subprocess.Next(this);
					}
					else
					{
						sExecutionStack.Peek().CurrentShape.Next(this);
					}
				}
			}
			catch (Exception eException)
			{
				bInterrupt = true;

				MetamorphManager.Instance.EventManager.Notify(this, eException);
			}
		}

		public void Start()
		{
			bInterrupt = false;

			Next();
		}

		public void Next(IEvent eEvent, ExecutionContext ecExecutionContext)
		{
			try
			{
				if (eEvent.CanGo(this))
				{
					bool bIsCurrentContext = false;

					while (!bIsCurrentContext && sExecutionStack.Count > 0)
					{
						if (sExecutionStack.Peek() == ecExecutionContext)
						{
							bIsCurrentContext = true;
						}
						else
						{
							MetamorphManager.Instance.EventManager.Unsubscribe(sExecutionStack.Peek().Events, this);

							sExecutionStack.Pop();
						}
					}

					if (EventType.Intermediate.Equals(eEvent.EventType) &&
						sExecutionStack.Count > 1)
					{
						MetamorphManager.Instance.EventManager.Unsubscribe(sExecutionStack.Peek().Events, this);

						sExecutionStack.Pop();
					}

					sExecutionStack.Peek().InitialShape = eEvent;

					bInterrupt = false;

					Next();
				}
			}
			catch (Exception eException)
			{
				bInterrupt = true;

				MetamorphManager.Instance.EventManager.Notify(this, eException);
			}
		}

		public string Id
		{
			get
			{
				return sId;
			}
		}

		public string Name
		{
			get
			{
				return pProcess.Name;
			}
		}

		public bool Interrupt
		{
			get
			{
				return bInterrupt;
			}
			set
			{
				bInterrupt = value;
			}
		}

		public IShape CurrentShape
		{
			get
			{
				return sCurrentShape;
			}
			set
			{
				sCurrentShape = value;
			}
		}

		public Stack<ExecutionContext> ExecutionStack
		{
			get
			{
				return sExecutionStack;
			}
			set
			{
				sExecutionStack = value;
			}
		}

		public object CurrentMessage
		{
			get
			{
				return oCurrentMessage;
			}
			set
			{
				oCurrentMessage = value;
			}
		}

		public Exception CurrentExeption
		{
			get
			{
				return eCurrentExeption;
			}
			set
			{
				eCurrentExeption = value;
			}
		}

		public IProcessContext ProcessContext
		{
			get
			{
				return pcProcessContext;
			}
			set
			{
				pcProcessContext = value;
				Init();
			}
		}

		#endregion
	}
}
